﻿using System;
using System.Text;
using System.Windows.Threading;

namespace MVVMFramework.ViewModel
{
    /// <summary>
    /// This is the root class in the hierarchy of the view models, which is why it implements the commonly used functionality
    /// </summary>
    public abstract class ViewModelBase : NotifyPropertyChangedBase, IViewModel
    {
        private bool lookupsAreLoaded;

        #region Events

        /// <summary>
        /// Indicates to the related View(s) that it should be closed.
        /// </summary>
        public event Action<bool?> ViewCloseRequest;

        #endregion

        protected bool LoadingLookups
        {
            get;
            private set;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
        /// </summary>
        protected ViewModelBase()
            : this(Dispatcher.CurrentDispatcher)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
        /// </summary>
        protected ViewModelBase(Dispatcher dispather)
            : base(dispather)
        {
            setDependencies();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
        }

        /// <summary>
        /// Refreshes lookups.
        /// </summary>
        /// <param name="force"></param>
        public void RefreshLookups(bool force)
        {
            if (force || !lookupsAreLoaded)
            {
                try
                {
                    LoadingLookups = true;

                    LoadLookups();
                    lookupsAreLoaded = true;
                }
                finally
                {
                    LoadingLookups = false;
                }
            }
        }

        #region Protected Methods

        /// <summary>
        /// Called when the ViewModel registers dependencies.
        /// </summary>
        protected virtual void OnRegisterDependencies()
        {
        }

        /// <summary>
        /// Raises the <see cref="ViewCloseRequest"/> event.
        /// </summary>
        /// <param name="result">A <see cref="Nullable"/> value of type <see cref="Boolean"/> that specifies whether the activity should be accepted (true) or canceled (false).</param>
        protected void RaiseViewCloseRequest(bool? result)
        {
            var handler = ViewCloseRequest;
            if (handler != null)
                handler.Invoke(result);
        }

        protected virtual void LoadLookups()
        {

        }

        /// <summary>
        /// Gets the detailed exception message.
        /// </summary>
        /// <param name="ex">The exception.</param>
        /// <returns></returns>
        protected virtual string GetExceptionMessage(Exception ex, bool includeStackTrace)
        {
            StringBuilder sb = new StringBuilder();

            Exception currentEx = ex;
            while (currentEx != null)
            {
                sb.AppendLine(currentEx.Message);

                currentEx = currentEx.InnerException;
            }

            if (includeStackTrace)
            {
                sb.AppendLine("Stack Trace:");
                sb.AppendLine(ex.StackTrace);
            }

            return sb.ToString().Trim(' ', '\n', '\r');
        }

        #endregion

        #region Private Methods

        private void setDependencies()
        {
            OnRegisterDependencies();
        }

        #endregion
    }
}
